package com.ohuang.apkpatch

import java.io.File

object PatchGenerate {

    /**
     * baksmali   baksmali.jar路径  用于dex转smali
     * smaliJar  smali.jar路径  将smali转dex
     *
     */
    fun generatePatch(
        javaexe: String?,
        baksmali: String,
        smaliJar: String,
        oldApk: String,
        newApk: String,
        outputApk: String
    ) {
        var file = File(outputApk)
        if (file.exists()) {
            file.delete()
        }
        if (!file.parentFile.exists()) {
            file.parentFile.mkdirs()
        }
        val rootPath = file.parentFile.absolutePath
        val buildPath = "$rootPath/build"
        if (File(buildPath).exists()) {
            val delete = FileUtils.delete(File(buildPath))
            if (delete) {
                println("删除成功 $buildPath")
            } else {
                println("删除失败 $buildPath")
                return
            }
        }
        File(buildPath).mkdirs()
        println("----开始解压apk------")
        unzip(oldApk, "$buildPath/oldapk")
        unzip(newApk, "$buildPath/newapk")
        println("------开始生成资源差分文件-------")
        copyDifferenceFile("$buildPath/oldapk", "$buildPath/newapk", "$buildPath/outapk")
        println("------开始生成dex差分文件-------")
        find_dex("$buildPath/outapk") {
            it.delete()
        }
        find_dex("$buildPath/oldapk") {
            var path = "$buildPath/oldSmali/" + removeSuffix(it.name, ".dex")
            dexToSmali(javaexe, baksmali, it.absolutePath, path)
        }
        find_dex("$buildPath/newapk") {
            var path = "$buildPath/newSmali/" + removeSuffix(it.name, ".dex")
            dexToSmali(javaexe, baksmali, it.absolutePath, path)
        }
        outputDifferenceSmali("$buildPath/oldSmali", "$buildPath/newSmali", "$buildPath/outSmali")

        File("$buildPath/outSmali").listFiles()?.forEach {
            val path = "$buildPath/outapk/" + it.name + ".dex"
            smaliToDex(javaexe, smaliJar, it.absolutePath, path)
        }
        deleteFile("$buildPath/outapk/AndroidManifest.xml")
        println("-------生成补丁包apk--------")
        ZipUtil.toZip(outputApk, "$buildPath/outapk", true)
        println("-------生成补丁包完成----------")
    }

    /**
     * baksmali   baksmali.jar路径  用于dex转smali
     * smaliJar  smali.jar路径  将smali转dex
     *
     */
    fun generatePatchNoResAssets(
        javaexe: String?,
        baksmali: String,
        smaliJar: String,
        oldApk: String,
        newApk: String,
        outputApk: String
    ) {
        var file = File(outputApk)
        if (file.exists()) {
            file.delete()
        }
        if (!file.parentFile.exists()) {
            file.parentFile.mkdirs()
        }
        val rootPath = file.parentFile.absolutePath
        val buildPath = "$rootPath/build"
        if (File(buildPath).exists()) {
            var delete = FileUtils.delete(File(buildPath))
            if (delete) {
                println("删除成功 $buildPath")
            } else {
                println("删除失败 $buildPath")
                return
            }
        }
        File(buildPath).mkdirs()
        println("----开始解压apk------")
        unzip(oldApk, "$buildPath/oldapk")
        unzip(newApk, "$buildPath/newapk")
        println("------开始生成资源差分文件-------")
        copyDifferenceFile("$buildPath/oldapk", "$buildPath/newapk", "$buildPath/outapk")
        println("------开始生成dex差分文件-------")
        find_dex("$buildPath/outapk") {
            it.delete()
        }
        find_dex("$buildPath/oldapk") {
            var path = "$buildPath/oldSmali/" + removeSuffix(it.name, ".dex")
            dexToSmali(javaexe, baksmali, it.absolutePath, path)
        }
        find_dex("$buildPath/newapk") {
            var path = "$buildPath/newSmali/" + removeSuffix(it.name, ".dex")
            dexToSmali(javaexe, baksmali, it.absolutePath, path)
        }
        outputDifferenceSmali("$buildPath/oldSmali", "$buildPath/newSmali", "$buildPath/outSmali")
        deleteRSmali("$buildPath/outSmali")
        File("$buildPath/outSmali").listFiles()?.forEach {
            val path = "$buildPath/outapk/" + it.name + ".dex"
            smaliToDex(javaexe, smaliJar, it.absolutePath, path)
        }
        deleteFile("$buildPath/outapk/assets")
        deleteFile("$buildPath/outapk/res")
        deleteFile("$buildPath/outapk/resources.arsc")
        deleteFile("$buildPath/outapk/AndroidManifest.xml")
        deleteFile("$buildPath/outapk/META-INF")
        println("-------生成补丁包apk--------")
        ZipUtil.toZip(outputApk, "$buildPath/outapk", true)
        println("-------生成补丁包完成----------")
    }


    /**
     * baksmali   baksmali.jar路径  用于dex转smali
     * smaliJar  smali.jar路径  将smali转dex
     *
     */
    fun generateApkNoRes(javaexe: String?, baksmali: String, smaliJar: String, newApk: String, outputApk: String) {
        var file = File(outputApk)
        if (file.exists()) {
            file.delete()
        }
        if (!file.parentFile.exists()) {
            file.parentFile.mkdirs()
        }
        val rootPath = file.parentFile.absolutePath
        val buildPath = "$rootPath/build"
        if (File(buildPath).exists()) {
            var delete = FileUtils.delete(File(buildPath))
            if (delete) {
                println("删除成功 $buildPath")
            } else {
                println("删除失败 $buildPath")
                return
            }
        }
        File(buildPath).mkdirs()
        println("----开始解压apk------")
        unzip(newApk, "$buildPath/newapk")
        println("------开始处理dex文件-------")
        find_dex("$buildPath/outapk") {
            it.delete()
        }
        find_dex("$buildPath/newapk") {
            var path = "$buildPath/newSmali/" + removeSuffix(it.name, ".dex")
            dexToSmali(javaexe, baksmali, it.absolutePath, path)
        }
        copyPathAllFile("$buildPath/newSmali", "$buildPath/outSmali")
        deleteRSmali("$buildPath/outSmali")
        File("$buildPath/outapk").mkdirs()
        File("$buildPath/outSmali").listFiles()?.forEach {
            val path = "$buildPath/outapk/" + it.name + ".dex"
            smaliToDex(javaexe, smaliJar, it.absolutePath, path)
        }
        println("-------生成补丁包apk--------")
        ZipUtil.toZip(outputApk, "$buildPath/outapk", true)
        println("-------生成补丁包完成----------")
    }

    private fun find_dex(rootPath: String, lmb: (file: File) -> Unit) {
        val file = File(rootPath)
        file.listFiles()?.forEach {
            if (it.isFile && it.name.endsWith(".dex")) {
                lmb(it)
            }
        }
    }

    private fun deleteFile(path: String) {
        val file = File(path)
        FileUtils.delete(file)
    }


    /**
     * 输出smali差分
     */
    private fun outputDifferenceSmali(oldFile: String, newFile: String, outFile: String) {
        var map = HashMap<String, String>()
        File(oldFile).listFiles()?.forEach {
            findSmali(it.absolutePath, "", map)
        }

        File(newFile).listFiles()?.forEach {
            val outSmali = File("$outFile/${it.name}")
            outSmali.mkdirs()
            findAndCopyDifferentSmali(it.absolutePath, "", map, outSmali.absolutePath)
        }
    }

    private fun removeSuffix(s: String, suffix: String): String {
        var substringAfter = s.lastIndexOf(suffix)
        var subSequence = s.subSequence(0, substringAfter)
        return subSequence.toString()
    }

    private fun unzip(zip: String, out: String) {
        if (File(out).exists()) {
            File(out).delete()
        }
        ZipUtil.unzip(zip, out)
    }

    fun dexToSmali(javaexe: String?, baksmali: String, dexPath: String, smaliPath: String) {
        println("----dexToSmali-----$dexPath")
        var file = File(smaliPath)
        if (file.exists()) {
            println("删除文件${file.name}")
            var delete = FileUtils.delete(file)
            if (delete) {
                println("删除成功")
            } else {
                println("删除失败")
            }
        }
        var java: String = if (javaexe.isNullOrEmpty()) {
            "java"
        } else {
            javaexe.replace(" ", "\" \"")
        }
        val cmd =
            "$java -Dfile.encoding=utf-8 -jar  \"$baksmali\" d -o \"$smaliPath\"   \"${dexPath}\" "
        println(cmd)
        var arrayOf = arrayOf(
            "cmd.exe", "/c", cmd
        )

        var exec = ExecUtil.execAndPrint(
            arrayOf,
            30 * 60
        )
        println(exec)

    }

    fun smaliToDex(javaexe: String?, smaliJar: String, smaliPath: String, dexPath: String) {
        println("----smaliToDex-----$smaliPath")
        var file = File(dexPath)
        if (file.exists()) {
            println("删除文件${file.name}")
            var delete = FileUtils.delete(file)
            if (delete) {
                println("删除成功")
            } else {
                println("删除失败")
            }
        }
        var java: String = if (javaexe.isNullOrEmpty()) {
            "java"
        } else {
            javaexe.replace(" ", "\" \"")
        }
        val cmd =
            "$java -Dfile.encoding=utf-8 -jar  \"$smaliJar\" a --verbose -o \"$dexPath\"   \"${smaliPath}\" "
        println(cmd)
        var arrayOf = arrayOf(
            "cmd.exe", "/c", cmd
        )

        var exec = ExecUtil.execAndPrint(
            arrayOf,
            30 * 60
        )
        println(exec)
    }
}